﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ProceduralCity.Models;
using ProceduralCity.Textures;
using ProceduralCity.Utility;
using ProceduralCity.Models.Buildings;
using ProceduralCity.Textures.Walls;
using ProceduralCity.Textures.Windows;

namespace ProceduralCity
{
    class City
    {
        Ground ground;
        BuildingBase[] buildings;

        public City(GraphicsDevice device, int numWide, int numDeep, float gap, Vector3 minSize, Vector3 maxSize)
        {
            Vector3 blockSize = maxSize + new Vector3(gap);
            ground = new Ground(Vector3.Zero, numWide, numDeep, maxSize.X, maxSize.Z, gap);

            buildings = new BuildingBase[numDeep * numWide];
            for (int row = 0; row < numDeep; ++row)
            {
                for (int col = 0; col < numWide; ++col)
                {
                    int index = row * numWide + col;

                    // TODO: add logic for randomly generating chunk sizes
                    Vector3 chunkSize = new Vector3(32, 16, 32);

                    Vector3 lotCornerPosition = new Vector3(col * blockSize.X, 0, row * blockSize.Z);
                    float randomValue = Maths.RandomBetween(0, 1);
                    if (randomValue < (1.0 / 3))
                    {
                        // make a RectangularBuilding
                        RectangularBuilding newBuilding = RectangularBuilding.RandomGenerateCentered(minSize, maxSize, chunkSize);
                        newBuilding.Position = new Vector3(lotCornerPosition.X + maxSize.X / 2, 0, lotCornerPosition.Z + maxSize.Z / 2);

                        // set window textures
                        Vector3 buildingSizeInChunks = newBuilding.Size / chunkSize;
                        int buildingPerimeterInChunks = (int)(2 * buildingSizeInChunks.X + 2 * buildingSizeInChunks.Z);
                        SimpleWallTexture wallTexture = new SimpleWallTexture();
                        wallTexture.Initialize((int)chunkSize.Z, (int)chunkSize.Y, new SimpleWindowTexture(SimpleWindowTexture.WindowType.Random));
                        Texture2D walls = wallTexture.Generate((int)(buildingPerimeterInChunks), (int)(buildingSizeInChunks.Y));
                        Texture2D top = SolidTexture.Generate(device, 1, 1, Color.Black);
                        newBuilding.SetTextures(walls, top);

                        buildings[index] = newBuilding;
                    }
                    else if (randomValue < (2.0 / 3))
                    {
                        // make a BlockyBuilding
                        Vector3 position = new Vector3(
                            lotCornerPosition.X + maxSize.X / 2,
                            0,
                            lotCornerPosition.Z + maxSize.Z / 2);
                        buildings[index] = BlockyBuilding.RandomGenerate(position, maxSize, 5, chunkSize);
                    }
                    else
                    {
                        // make a RoundedBuilding
                        const int numSegments = 32;
                        float height = Maths.NearestMultiple(Maths.RandomBetween(0.5f, 1) * maxSize.Y, chunkSize.Y);
                        Vector3 halfSize = maxSize / 2;
                        BoundingBox buildingBounds = new BoundingBox(new Vector3(-halfSize.X, 0, -halfSize.Z), new Vector3(halfSize.X, height, halfSize.Z));
                        RoundedBuilding newBuilding = new RoundedBuilding(buildingBounds, numSegments);
                        newBuilding.Position = new Vector3(lotCornerPosition.X + halfSize.X, 0, lotCornerPosition.Z + halfSize.Z);

                        // set textures
                        SimpleWallTexture wallTexture = new SimpleWallTexture();
                        wallTexture.Initialize((int)chunkSize.X, (int)chunkSize.Y, null);
                        wallTexture.Generate(numSegments, (int)(height / chunkSize.Y));
                        Texture2D roofTexture = SolidTexture.Generate(device, 1, 1, Color.Black);
                        newBuilding.SetTextures(wallTexture.Texture, roofTexture);

                        buildings[index] = newBuilding;
                    }
                }
            }
        }

        public void Draw(GraphicsDevice device, BasicEffect groundEffect, BasicEffect buildingEffect)
        {
            ground.Draw(device, groundEffect);

            if (buildings != null)
            {
                foreach (BuildingBase building in buildings)
                    building.Draw(device, buildingEffect);
            }
        }
    }
}
